前言
最近在使用 Golang 实现 HNSW 算法的多属性支持,在其中用到的 map 结构。然后 Map 并不是线程安全的,使用多线程的话在新版本的 Golang 中会直接出现 panic。
sync.Map 这个数据结构是线程安全的,它填补了 Map 线程不安全的缺陷,不过最好只在需要的情况下使用。它一般用于并发模型中对同一类 map 结构体的读写,或其他适用于 sync.Map 的情况。
关于 sync.Map 的源码解析文章:Go 1.9 sync.Map揭秘
sync.Map使用方法
Store
存 key,valueLoadOrStore
取&存—具体看代码Load
取 key 对应的 valueRange
遍历所有的 key,valueDelete
删除 key,及其 value
Store(key, value interface{})
说明: 存储一个设置的键值。
LoadOrStore(key, value interface{}) (actual interface{}, loaded bool)
说明: 返回键的现有值(如果存在),否则存储并返回给定的值,如果是读取则返回true,如果是存储返回false。
Load(key interface{}) (value interface{}, ok bool)
说明: 读取存储在map中的值,如果没有值,则返回nil。OK的结果表示是否在map中找到值。
Delete(key interface{})
说明: 删除键对应的值。
Range(f func(key, value interface{}) bool)
说明: 循环读取map中的值。
代码如下
1 | package main |
实际运用时遇到的问题
1 | fatal error: sync: unlock of unlocked mutex |
在多线程时会遇到这样的错误,十分的随机且不可定位,在 Go 的 github 上找到这样类似问题的 issue,Go 的开发人员提到 sync.Map
的使用规则:
A Map must not be copied after first use.
Package sync provides basic synchronization primitives such as mutual exclusion locks. Other than the Once and WaitGroup types, most are intended for use by low-level library routines. Higher-level synchronization is better done via channels and communication.
Values containing the types defined in this package should not be copied.
创建完成的 sync.Map 是线程安全的,但是经过拷贝之后,两个 sync.Map 里面存储的是同一个 map(就是那个原生的,线程不安全的 map), mutex 无法起到保护作用,就线程不安全了。
参考:
sync.RWMutex+Map
利用传统的 sync.RWMutex+Map 实现并发安全的 Map:
1 | var rwmap = struct{ |
读数据时候,读锁锁定:
1 | rwmap.RLock() |
写数据的时候,写锁锁定:
1 | rwmap.Lock() |